package cn.jingyuan.owl.utils.core.codec;

/**
 * RotN（rotate by N places），回转 N 位密码，是一种简易的替换式密码，也是过去在古罗马开发的凯撒加密的一种变体。<br>
 * 代码来自：https://github.com/orclight/jencrypt
 */
public class Rot {

    private static final char CHAR_a = 'a';

    private static final char CHAR_z = 'z';

    private static final char CHAR_A = 'A';

    private static final char CHAR_Z = 'Z';

    private static final char CHAR_0 = '0';

    private static final char CHAR_9 = '9';

    /**
     * Rot-13 编码，同时编码数字
     *
     * @param message 被编码的消息
     *
     * @return 编码后的字符串
     */
    public static String encode13(String message) {
        return encode13(message, true);
    }

    /**
     * Rot-13 编码
     *
     * @param message 被编码的消息
     * @param isEncodeNumber 是否编码数字
     *
     * @return 编码后的字符串
     */
    public static String encode13(String message, boolean isEncodeNumber) {
        return encode(message, 13, isEncodeNumber);
    }

    /**
     * RotN 编码
     *
     * @param message 被编码的消息
     * @param offset 位移，常用位移 13
     * @param isEncodeNumber 是否编码数字
     *
     * @return 编码后的字符串
     */
    public static String encode(String message, int offset, boolean isEncodeNumber) {
        final int len = message.length();
        final char[] chars = new char[len];

        for (int i = 0; i < len; i++) {
            chars[i] = encodeChar(message.charAt(i), offset, isEncodeNumber);
        }
        return new String(chars);
    }

    /**
     * Rot-13 解码，同时解码数字
     *
     * @param rot 被解码的消息密文
     *
     * @return 解码后的字符串
     */
    public static String decode13(String rot) {
        return decode13(rot, true);
    }

    /**
     * Rot-13 解码
     *
     * @param rot 被解码的消息密文
     * @param isDecodeNumber 是否解码数字
     *
     * @return 解码后的字符串
     */
    public static String decode13(String rot, boolean isDecodeNumber) {
        return decode(rot, 13, isDecodeNumber);
    }

    /**
     * RotN 解码
     *
     * @param rot 被解码的消息密文
     * @param offset 位移，常用位移 13
     * @param isDecodeNumber 是否解码数字
     *
     * @return 解码后的字符串
     */
    public static String decode(String rot, int offset, boolean isDecodeNumber) {
        final int len = rot.length();
        final char[] chars = new char[len];

        for (int i = 0; i < len; i++) {
            chars[i] = decodeChar(rot.charAt(i), offset, isDecodeNumber);
        }
        return new String(chars);
    }

    /**
     * 解码字符
     *
     * @param c 字符
     * @param offset 位移
     * @param isDecodeNumber 是否解码数字
     *
     * @return 解码后的字符串
     */
    private static char encodeChar(char c, int offset, boolean isDecodeNumber) {
        if (isDecodeNumber) {
            if (c >= CHAR_0 && c <= CHAR_9) {
                c -= CHAR_0;
                c = (char) ((c + offset) % 10);
                c += CHAR_0;
            }
        }

        // A == 65, Z == 90
        if (c >= CHAR_A && c <= CHAR_Z) {
            c -= CHAR_A;
            c = (char) ((c + offset) % 26);
            c += CHAR_A;
        }
        // a == 97, z == 122.
        else if (c >= CHAR_a && c <= CHAR_z) {
            c -= CHAR_a;
            c = (char) ((c + offset) % 26);
            c += CHAR_a;
        }
        return c;
    }

    /**
     * 编码字符
     *
     * @param c 字符
     * @param offset 位移
     * @param isDecodeNumber 是否编码数字
     *
     * @return 编码后的字符串
     */
    private static char decodeChar(char c, int offset, boolean isDecodeNumber) {
        int temp = c;
        // if converting numbers is enabled
        if (isDecodeNumber) {
            if (temp >= CHAR_0 && temp <= CHAR_9) {
                temp -= CHAR_0;
                temp = temp - offset;
                while (temp < 0) {
                    temp += 10;
                }
                temp += CHAR_0;
            }
        }

        // A == 65, Z == 90
        if (temp >= CHAR_A && temp <= CHAR_Z) {
            temp -= CHAR_A;

            temp = temp - offset;
            while (temp < 0) {
                temp = 26 + temp;
            }
            temp += CHAR_A;
        } else if (temp >= CHAR_a && temp <= CHAR_z) {
            temp -= CHAR_a;

            temp = temp - offset;
            if (temp < 0) {
                temp = 26 + temp;
            }

            temp += CHAR_a;
        }
        return (char) temp;
    }

}
